﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Text;
using System.Linq.Dynamic;
using SWGeneracionE.Libraries;
using SWGeneracionE.Models;
using System.Globalization;

namespace SWGeneracionE.Controllers
{
    public class PagoController : BaseController
    {
        //
        // GET: /Pago/PagosEvento

        [Authorize]
        public ActionResult PagosEvento()
        {
            return View();
        }

        //
        // GET: /Pago/PagosBus

        [Authorize]
        public ActionResult PagosBus()
        {
            return View();
        }

        //
        // GET: /Pago/PagoRecaudacion

        [Authorize]
        public ActionResult PagosRecaudacion()
        {
            return View();
        }

        #region "Acciones Estado Cuenta"
        [HttpPost]
        public JsonResult ECPagoList(int idParticipante = 0)
        {
            try
            {
                int idEvento = int.Parse(ViewBag.Evento);
                var query = UoW.pagoRepository.FindBy(p => (p.ParticipanteXEvento.idParticipante == idParticipante)
                    && (p.ParticipanteXEvento.idEvento == idEvento)
                    && ((p.Movimiento == null) || (p.Movimiento.idMotivo == Constantes.MOTIVODEPOSITO_EVENTO)))
                    .OrderBy(p => p.fechaPago)
                    .ToList();

                var pagos = query.Select((p, index) => new
                {
                    noPago = index + 1,
                    fecha = p.fechaPago.ToString(Constantes.FORMATO_FECHA),
                    moneda = p.montoLOC != null ? Constantes.SIMBOLO_LOC : Constantes.SIMBOLO_USD,
                    monto = p.montoLOC != null ? ((decimal)p.montoLOC).ToString(Constantes.SPECIFIER_N)
                                : ((decimal)p.montoUSD).ToString(Constantes.SPECIFIER_N),
                    tipo = Constantes.TipoPago(p.idTipoPago),
                    documento = p.noDocumento != null ? p.noDocumento : string.Empty,
                    observaciones = p.observaciones != null ? p.observaciones : string.Empty,
                    estado = Constantes.EstadoPago(p.estado),
                    boleta = RequiereBoleta(p.idTipoPago) ? p.Movimiento.noDocumento : string.Empty
                });
                var pagosCount = pagos.Count();
                return Json(new { Result = "OK", Records = pagos, TotalRecordCount = pagosCount });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error PagoList: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener listado de pagos." });
            }
        }

        [HttpPost]
        public JsonResult GetPagoBus(int idPagoBus)
        {
            try
            {
                var pago = UoW.pagoRepository.FindFirst(p => p.idPago == idPagoBus);
                var pagobus = new
                {
                    motivo = "Pago de Bus",
                    noPago = 1,
                    fecha = pago.fechaPago.ToString(Constantes.FORMATO_FECHA),
                    moneda = pago.montoLOC != null ? Constantes.SIMBOLO_LOC : Constantes.SIMBOLO_USD,
                    monto = pago.montoLOC != null ? ((decimal)pago.montoLOC).ToString(Constantes.SPECIFIER_N)
                                : ((decimal)pago.montoUSD).ToString(Constantes.SPECIFIER_N),
                    tipo = Constantes.TipoPago(pago.idTipoPago),
                    documento = pago.noDocumento != null ? pago.noDocumento : string.Empty,
                    observaciones = pago.observaciones != null ? pago.observaciones : string.Empty,
                    estado = Constantes.EstadoPago(pago.estado),
                    boleta = RequiereBoleta(pago.idTipoPago) ? pago.Movimiento.noDocumento : string.Empty
                };
                return Json(new { Result = "OK", Record = pagobus });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error GetPagoBus: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener pago de bus." });
            }
        }

        [HttpPost]
        public JsonResult ECPorCorreo(int idParticipanteXEvento)
        {
            try
            {
                EnviarCorreoEC(idParticipanteXEvento);
                return Json(new { Result = "OK", Message = "Se ha enviado el estado de cuenta por correo exitosamente." });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error ECPorCorreo: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al enviar estado de cuenta." });
            }
        }

        [HttpPost]
        public JsonResult GetEstadoCuenta(int idParticipante)
        {
            try
            {
                int idEvento = int.Parse(ViewBag.Evento);
                var participante = UoW.participanteXEventoRepository.FindFirst(p => (p.idEvento == idEvento) && (p.idParticipante == idParticipante));
                var pagobus = participante.Pago.Where(p =>
                    (p.Movimiento != null)
                    && (p.Movimiento.idMotivo == Constantes.MOTIVODEPOSITO_BUS) 
                    && (p.estado == Constantes.ESTADOPAGO_AUTORIZADO))
                    .Select(p => new
                    {
                        idPago = p.idPago,
                        montoLOC = p.montoLOC
                    }).FirstOrDefault();

                bool bBus = false;
                bool bPagoBus = false;
                int idPagoBus = 0;
                if (participante.tipoTransporte == Constantes.TIPOTRANSPORTE_BUS)
                {
                    bBus = true;
                    if (pagobus != null)
                    {
                        bPagoBus = true;
                        idPagoBus = pagobus.idPago;
                    }
                }

                decimal sumLOC = participante.AbonoTotalLOC;
                decimal sumUSD = participante.AbonoTotalUSD;
                decimal sumRecaudacion = participante.RecaudacionTotal;
                decimal montoTotal = 0;
                string moneda = string.Empty;
                decimal subtotal = 0;
                if (sumUSD > 0)
                {
                    subtotal = sumUSD;
                    moneda = Constantes.SIMBOLO_USD;
                }
                else
                {
                    subtotal = sumLOC;
                    moneda = Constantes.SIMBOLO_LOC;
                }
                montoTotal = sumLOC + (sumUSD * Constantes.TASA_APROX) + sumRecaudacion;

                var estadocuenta = new
                {
                    subtotal = subtotal.ToString(Constantes.SPECIFIER_N),
                    recaudacion = sumRecaudacion.ToString(Constantes.SPECIFIER_N),
                    moneda = moneda,
                    total = montoTotal.ToString(Constantes.SPECIFIER_N),
                    bus = bBus,
                    pagobus = bPagoBus,
                    idPagoBus = idPagoBus,
                    idParticipante = participante.idParticipanteXEvento
                };
                return Json(new { Result = "OK", Record = estadocuenta });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error GetPagoBus: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener pago de bus." });
            }
        }
        #endregion

        #region "Acciones Pago"
        [HttpPost]
        public JsonResult CreatePago(Pago pago)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    AgregarBitacora("Error CreatePago: Formulario inválido");
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "¡El formulario no es válido! " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((pago.montoLOC == null && pago.montoUSD == null) ||
                    (pago.montoLOC != null && pago.montoUSD != null))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "Debe ingresar monto en dólares o quetzales (no ambos). " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((pago.montoLOC == null && pago.montoUSD <= 0) ||
                    (pago.montoUSD == null && pago.montoLOC <= 0))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto ingresado debe ser mayor que cero. " +
                        "Corrija e intente nuevemente."
                    });
                }

                //comparar participante
                if (pago.codigoParticipante != null)
                {
                    ParticipanteXEvento participante = UoW.participanteXEventoRepository
                        .FindFirst(t => t.codigo == pago.codigoParticipante.Trim() && t.estado == Constantes.ESTADOPARTICIPANTE_INSCRITO);
                    if (participante != null)
                        pago.idParticipanteXEvento = participante.idParticipanteXEvento;
                    else
                    {
                        return Json(new
                        {
                            Result = "ERROR",
                            Message = "¡El participante que ha ingresado es inv&aacute;lido! <br/>" +
                                      "Corrija e intente nuevemente."
                        });
                    }
                }

                //comparar contra movimiento
                Movimiento movimiento = UoW.movimientoRepository.FindFirst(m => m.idMovimiento == pago.idMovimiento);
                if (movimiento.estado != Constantes.ESTADOMOVIMIENTO_INGRESADO)
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "No se puede modificar un movimiento que ya ha sido autorizado/rechazado."
                    });
                }
                if ((movimiento.montoLOC != null && pago.montoUSD != null) ||
                   (movimiento.montoUSD != null && pago.montoLOC != null))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto ingresado no es de la misma moneda que el movimiento. " +
                        "Corrija e intente nuevemente."
                    });
                }
                decimal total = movimiento.montoLOC != null ? 
                    (decimal)movimiento.Pago.Sum(m => m.montoLOC) 
                    : (decimal)movimiento.Pago.Sum(m => m.montoUSD);
                decimal montoM = movimiento.montoLOC != null ? (decimal)movimiento.montoLOC : (decimal)movimiento.montoUSD;
                decimal montoP = pago.montoLOC != null ? (decimal)pago.montoLOC : (decimal)pago.montoUSD;
                decimal excedente = montoM - total;
                if (excedente < montoP)
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto excede el límite del movimiento (" + 
                        excedente.ToString(Constantes.SPECIFIER_N) + "). " +
                        "Corrija e intente nuevemente."
                    });
                }

                //Agregar valores default
                pago.estado = Constantes.ESTADOPAGO_INGRESADO;
                //Insertar pago
                UoW.pagoRepository.Add(pago);
                UoW.Save();
                //seleccionar solo los campos que se necesitan, ya que los demás no pueden enviarse por Json
                var addedPago = new
                {
                    idMovimiento = pago.idMovimiento,
                    idPago = pago.idPago,
                    fechaPago = pago.fechaPago,
                    montoUSD = pago.montoUSD,
                    montoLOC = pago.montoLOC,
                    idTipoPago = pago.idTipoPago,
                    noDocumento = pago.noDocumento,
                    observaciones = pago.observaciones,
                    codigoParticipante = pago.ParticipanteXEvento.codigo
                };
                return Json(new { Result = "OK", Record = addedPago });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error CreatePago: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al crear pago." });
            }
        }

        [HttpPost]
        public JsonResult UpdatePago(Pago pago)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    AgregarBitacora("Error UpdatePago: Formulario inválido");
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "¡El formulario no es válido! " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((pago.montoLOC == null && pago.montoUSD == null) ||
                    (pago.montoLOC != null && pago.montoUSD != null))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "Debe ingresar monto en dólares o quetzales (no ambos). " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((pago.montoLOC == null && pago.montoUSD <= 0) ||
                    (pago.montoUSD == null && pago.montoLOC <= 0))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto ingresado debe ser mayor que cero. " +
                        "Corrija e intente nuevemente."
                    });
                }

                //comparar participante
                if (pago.codigoParticipante != null)
                {
                    ParticipanteXEvento participante = UoW.participanteXEventoRepository
                        .FindFirst(t => t.codigo == pago.codigoParticipante.Trim() && t.estado == Constantes.ESTADOPARTICIPANTE_INSCRITO);
                    if (participante != null)
                        pago.idParticipanteXEvento = participante.idParticipanteXEvento;
                    else
                    {
                        return Json(new
                        {
                            Result = "ERROR",
                            Message = "¡El participante que ha ingresado es inv&aacute;lido! <br/>" +
                                      "Corrija e intente nuevemente."
                        });
                    }
                }

                //comparar contra movimiento
                Pago pagoA = UoW.pagoRepository.FindFirst(p => p.idPago == pago.idPago);
                Movimiento movimiento = UoW.movimientoRepository.FindFirst(m => m.idMovimiento == pago.idMovimiento);
                if (movimiento.estado != Constantes.ESTADOMOVIMIENTO_INGRESADO)
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "No se puede modificar un movimiento que ya ha sido autorizado/rechazado."
                    });
                }
                if ((movimiento.montoLOC != null && pago.montoUSD != null) ||
                   (movimiento.montoUSD != null && pago.montoLOC != null))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto ingresado no es de la misma moneda que el movimiento. " +
                        "Corrija e intente nuevemente."
                    });
                }
                decimal total = movimiento.montoLOC != null ?
                    (decimal)movimiento.Pago.Sum(m => m.montoLOC)
                    : (decimal)movimiento.Pago.Sum(m => m.montoUSD);
                decimal montoM = movimiento.montoLOC != null ? (decimal)movimiento.montoLOC : (decimal)movimiento.montoUSD;
                decimal montoP = pago.montoLOC != null ? (decimal)pago.montoLOC : (decimal)pago.montoUSD;
                decimal montoPA = pagoA.montoLOC != null ? (decimal)pagoA.montoLOC : (decimal)pagoA.montoUSD;
                decimal excedente = montoM - (total - montoPA);
                if (excedente < montoP)
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto excede el límite del movimiento (" +
                        excedente.ToString(Constantes.SPECIFIER_N) + "). " +
                        "Corrija e intente nuevemente."
                    });
                }

                //Agregar valores default
                pago.estado = pagoA.estado;
                //Actualizar pago
                UoW.pagoRepository.Detach(pagoA);
                UoW.pagoRepository.Edit(pago);
                UoW.Save();
                AgregarBitacora("Actualizacion de pago (UpdatePago): " + pago.idPago.ToString());
                return Json(new { Result = "OK" });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error UpdatePago: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al actualizar pago." });
            }
        }

        [HttpPost]
        public JsonResult DeletePago(int idPago)
        {
            try
            {
                Pago pago = UoW.pagoRepository.FindFirst(p => p.idPago == idPago);
                Movimiento movimiento = UoW.movimientoRepository.FindFirst(m => m.idMovimiento == pago.idMovimiento);
                if (movimiento.estado != Constantes.ESTADOMOVIMIENTO_INGRESADO)
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "No se puede modificar un movimiento que ya ha sido autorizado/rechazado."
                    });
                }
                UoW.pagoRepository.Delete(pago);
                UoW.Save();
                AgregarBitacora("Eliminación de pago (DeletePago): " + idPago.ToString());
                return Json(new { Result = "OK" });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error DeletePago: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al eliminar pago." });
            }
        }

        [HttpPost]
        public JsonResult PagoList(int idMovimiento)
        {
            try
            {
                var pagos = UoW.pagoRepository.FindBy(d => d.idMovimiento == idMovimiento)
                    .Select(p => new
                    {
                        idMovimiento = p.idMovimiento,
                        idPago = p.idPago,
                        fechaPago = p.fechaPago,
                        montoUSD = p.montoUSD,
                        montoLOC = p.montoLOC,
                        idTipoPago = p.idTipoPago,
                        noDocumento = p.noDocumento,
                        observaciones = p.observaciones,
                        codigoParticipante = p.ParticipanteXEvento.codigo
                    }).ToList();
                var pagosCount = pagos.Count;
                return Json(new { Result = "OK", Records = pagos, TotalRecordCount = pagosCount });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error PagoList: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener listado de pagos." });
            }
        }

        [HttpPost]
        public JsonResult GetTipoPagoOptions(int idTipoMovimiento)
        {
            try
            {
                var tipos = UoW.tipoPagoRepository.GetAll()
                    .FindAll(t => t.idTipoMovimiento == idTipoMovimiento)
                    .Select(e => new { DisplayText = e.nombre, Value = e.idTipoPago });
                return Json(new { Result = "OK", Options = tipos });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error GetTipoPagoOptions: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener listado de tipos de pago" });
            }
        }
        #endregion

        #region "Acciones Movimiento"
        [HttpPost]
        public JsonResult CreateMovimiento(Movimiento movimiento)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    AgregarBitacora("Error CreateMovimiento: Formulario inválido");
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "¡El formulario no es válido! " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((movimiento.montoLOC == null && movimiento.montoUSD == null) ||
                    (movimiento.montoLOC != null && movimiento.montoUSD != null))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "Debe ingresar monto en dólares o quetzales (no ambos). " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((movimiento.montoLOC == null && movimiento.montoUSD <= 0) ||
                    (movimiento.montoUSD == null && movimiento.montoLOC <= 0))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto ingresado debe ser mayor que cero. " +
                        "Corrija e intente nuevemente."
                    });
                }

                bool AutorizarRechazar = AccionPermitida((List<webpages_Menu>)ViewBag.Menu, "AutorizarRechazar", "Pago");
                movimiento.estado = Constantes.ESTADOMOVIMIENTO_INGRESADO;
                movimiento.idEvento = int.Parse(ViewBag.Evento);
                var mv = UoW.movimientoRepository.Add(movimiento);
                var addedMovimiento = new
                             {
                                 idMovimiento = mv.idMovimiento,
                                 fecha = mv.fecha,
                                 estado = mv.estado,
                                 idTipoPago = mv.idTipoPago,
                                 montoUSD = mv.montoUSD,
                                 montoLOC = mv.montoLOC,
                                 noDocumento = mv.noDocumento,
                                 observaciones = mv.observaciones,
                                 encargado = mv.encargado,
                                 idMotivo = mv.idMotivo,
                                 AutorizarRechazar = AutorizarRechazar
                             };
                UoW.Save();
                AgregarBitacora("Creación de movimiento (CreateMovimiento): " + addedMovimiento.idMovimiento.ToString());
                return Json(new { Result = "OK", Record = addedMovimiento });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error CreateMovimiento: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al crear movimiento." });
            }
        }

        [HttpPost]
        public JsonResult UpdateMovimiento(Movimiento movimiento)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    AgregarBitacora("Error UpdateMovimiento: Formulario inválido");
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "¡El formulario no es válido! " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((movimiento.montoLOC == null && movimiento.montoUSD == null) ||
                    (movimiento.montoLOC != null && movimiento.montoUSD != null))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "Debe ingresar monto en dólares o quetzales (no ambos). " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((movimiento.montoLOC == null && movimiento.montoUSD <= 0) ||
                    (movimiento.montoUSD == null && movimiento.montoLOC <= 0))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto ingresado debe ser mayor que cero. " +
                        "Corrija e intente nuevemente."
                    });
                }

                Movimiento movimientoA = UoW.movimientoRepository.FindFirst(m => m.idMovimiento == movimiento.idMovimiento);
                if (movimientoA.estado != Constantes.ESTADOMOVIMIENTO_INGRESADO)
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "No se puede modificar un movimiento que ya ha sido autorizado/rechazado."
                    });
                }

                movimiento.estado = movimientoA.estado;
                movimiento.idEvento = movimientoA.idEvento;
                UoW.movimientoRepository.Detach(movimientoA);
                UoW.movimientoRepository.Edit(movimiento);
                UoW.Save();
                AgregarBitacora("Actualización de movimiento (UpdateMovimiento): " + movimiento.idMovimiento.ToString());
                return Json(new { Result = "OK" });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error UpdateMovimiento: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al actualizar movimiento." });
            }
        }

        [HttpPost]
        public JsonResult DeleteMovimiento(int idMovimiento)
        {
            try
            {
                Movimiento movimiento = UoW.movimientoRepository.FindFirst(p => p.idMovimiento == idMovimiento);
                if (movimiento.estado != Constantes.ESTADOMOVIMIENTO_INGRESADO)
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "No se puede modificar un movimiento que ya ha sido autorizado/rechazado."
                    });
                }
                UoW.movimientoRepository.Delete(movimiento);
                UoW.Save();
                AgregarBitacora("Eliminación de movimiento (DeleteMovimiento): " + idMovimiento.ToString());
                return Json(new { Result = "OK" });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error DeleteMovimiento: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al eliminar movimiento." });
            }
        }

        [HttpPost]
        public JsonResult MovimientoList(string estado, string moneda, string documento, string encargado, 
                                         string fecha, string motivo = null, int jtStartIndex = 0, 
                                         int jtPageSize = 0, string jtSorting = null)
        {
            try
            {
                int idEvento = int.Parse(ViewBag.Evento);
                if (string.IsNullOrEmpty(jtSorting))
                    jtSorting = "fecha ASC";
                bool AutorizarRechazar = AccionPermitida((List<webpages_Menu>)ViewBag.Menu, "AutorizarRechazar", "Pago");
                var query = MovimientoListByFilter(estado, moneda, documento, encargado, fecha, motivo);
                var queryP = query.OrderAndPaging(jtStartIndex, jtPageSize, jtSorting);
                var movimientos = queryP.ToList()
                             .Select(m => new
                             {
                                 idMovimiento = m.idMovimiento,
                                 fecha = m.fecha,
                                 estado = m.estado,
                                 idTipoPago = m.idTipoPago,
                                 montoUSD = m.montoUSD,
                                 montoLOC = m.montoLOC,
                                 noDocumento = m.noDocumento,
                                 observaciones = m.observaciones,
                                 encargado = m.encargado,
                                 idMotivo = m.idMotivo,
                                 AutorizarRechazar = AutorizarRechazar
                             });
                var movimientosCount = query.Count();

                return Json(new { Result = "OK", Records = movimientos, TotalRecordCount = movimientosCount });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error MovimientoList: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener listado de movimientos." });
            }
        }

        public ExcelResult MovimientoExportExcel(string estado, string moneda, string documento, string encargado, 
                                                 string fecha, string motivo = null)
        {
            try
            {
                int idEvento = int.Parse(ViewBag.Evento);
                var query = MovimientoListByFilter(estado, moneda, documento, encargado, fecha, motivo).ToList();
                var contador = query.Count;

                //se crea el objeto con los encabezados y datos que iran en la hoja de excel
                var excelData = new ExcelData();
                excelData.headers = new string[9];
                excelData.contents = new string[contador, 9];
                excelData.headers[0] = "Fecha";
                excelData.headers[1] = "Estado";
                excelData.headers[2] = "Tipo";
                excelData.headers[3] = "Monto " + Constantes.SIMBOLO_LOC;
                excelData.headers[4] = "Monto " + Constantes.SIMBOLO_USD;
                excelData.headers[5] = "No. Documento";
                excelData.headers[6] = "Encargado";
                excelData.headers[7] = "Participantes";
                excelData.headers[8] = "Observaciones";

                var i = 0;
                //se llenan los datos según los obtenidos en la consulta
                foreach (var movimiento in query)
                {
                    StringBuilder sbParticipantes = new StringBuilder();
                    excelData.contents[i, 0] = movimiento.fecha.ToString(Constantes.FORMATO_FECHA);
                    excelData.contents[i, 1] = Constantes.EstadoMovimiento(movimiento.estado);
                    excelData.contents[i, 2] = Constantes.TipoMovimiento(movimiento.idTipoPago);
                    excelData.contents[i, 3] = movimiento.montoLOC != null ? ((decimal)movimiento.montoLOC).ToString(Constantes.SPECIFIER_N) : string.Empty;
                    excelData.contents[i, 4] = movimiento.montoUSD != null ? ((decimal)movimiento.montoUSD).ToString(Constantes.SPECIFIER_N) : string.Empty;
                    excelData.contents[i, 5] = movimiento.noDocumento;
                    excelData.contents[i, 6] = movimiento.encargado != null ? movimiento.encargado : string.Empty;
                    foreach (var pago in movimiento.Pago)
                    {
                        if (sbParticipantes.Length > 0)
                            sbParticipantes.Append(", ");
                        sbParticipantes.Append(pago.ParticipanteXEvento.codigo);
                    }
                    excelData.contents[i, 7] = sbParticipantes.ToString();
                    excelData.contents[i, 8] = movimiento.observaciones != null ? movimiento.observaciones : string.Empty;

                    i++;
                }

                //Devolver vista parcial
                return new ExcelResult("MovimientoGE" + DateTime.Now.ToString(Constantes.FORMATO_FECHA_ARCHIVO) + ".xls", PartialView("_ExcelTemplate", excelData));
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error MovimientoExportExcel: " + ex.Message + "--" + ex.Source);
                return null;
            }
        }

        public ExcelResult MovimientoRExportExcel(string estado, string moneda, string documento, string encargado,
                                                 string fecha, string motivo = null)
        {
            try
            {
                int idEvento = int.Parse(ViewBag.Evento);
                var query = MovimientoListByFilter(estado, moneda, documento, encargado, fecha, motivo).ToList();
                var contador = query.Count;

                //se crea el objeto con los encabezados y datos que iran en la hoja de excel
                var excelData = new ExcelData();
                excelData.headers = new string[7];
                excelData.contents = new string[contador, 7];
                excelData.headers[0] = "Fecha";
                excelData.headers[1] = "Estado";
                excelData.headers[2] = "Tipo";
                excelData.headers[3] = "Monto " + Constantes.SIMBOLO_LOC;
                excelData.headers[4] = "No. Documento";
                excelData.headers[5] = "Encargado";
                excelData.headers[6] = "Observaciones";

                var i = 0;
                //se llenan los datos según los obtenidos en la consulta
                foreach (var movimiento in query)
                {
                    StringBuilder sbParticipantes = new StringBuilder();
                    excelData.contents[i, 0] = movimiento.fecha.ToString(Constantes.FORMATO_FECHA);
                    excelData.contents[i, 1] = Constantes.EstadoMovimiento(movimiento.estado);
                    excelData.contents[i, 2] = Constantes.TipoMovimiento(movimiento.idTipoPago);
                    excelData.contents[i, 3] = movimiento.montoLOC != null ? ((decimal)movimiento.montoLOC).ToString(Constantes.SPECIFIER_N) : string.Empty;
                    excelData.contents[i, 4] = movimiento.noDocumento;
                    excelData.contents[i, 5] = movimiento.encargado != null ? movimiento.encargado : string.Empty;
                    excelData.contents[i, 6] = movimiento.observaciones != null ? movimiento.observaciones : string.Empty;

                    i++;
                }

                //Devolver vista parcial
                return new ExcelResult("MovimientoRGE" + DateTime.Now.ToString(Constantes.FORMATO_FECHA_ARCHIVO) + ".xls", PartialView("_ExcelTemplate", excelData));
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error MovimientoRExportExcel: " + ex.Message + "--" + ex.Source);
                return null;
            }
        }

        [HttpPost]
        public JsonResult GetEstadoMovimientoOptions()
        {
            try
            {
                var estados = UoW.estadoMovimientoRepository.GetAll().Select(
                                e => new { DisplayText = e.nombre, Value = e.idEstadoMovimiento });
                return Json(new { Result = "OK", Options = estados });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error GetEstadoMovimientoOptions: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener listado de estados de movimiento" });
            }
        }

        [HttpPost]
        public JsonResult GetTipoMovimientoOptions(int idMotivo)
        {
            try
            {
                var tipos = UoW.tipoMovimientoRepository
                    .FindBy(t =>
                        (idMotivo == Constantes.MOTIVODEPOSITO_EVENTO)
                        || ((idMotivo == Constantes.MOTIVODEPOSITO_BUS) && ( (t.idTipoMovimiento == Constantes.TIPOMOVIMIENTO_DEPOSITOBANCARIO) || (t.idTipoMovimiento == Constantes.TIPOMOVIMIENTO_TRANSFERENCIABANCARIA)))
                        || ((idMotivo == Constantes.MOTIVODEPOSITO_RECAUDACION) && (t.idTipoMovimiento == Constantes.TIPOMOVIMIENTO_DEPOSITOBANCARIO))
                    )
                    .Select(e => new { DisplayText = e.nombre, Value = e.idTipoMovimiento });
                return Json(new { Result = "OK", Options = tipos });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error GetTipoMovimientoOptions: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener listado de tipos de movimiento" });
            }
        }

        private IQueryable<Movimiento> MovimientoListByFilter(string estado, string moneda, string documento,
                                                                string encargado, string fecha, string motivo)
        {
            var query = UoW.movimientoRepository.GetAll();

            if (!string.IsNullOrEmpty(estado))
            {
                int iEstado = int.Parse(estado);
                query = query.Where(p => p.estado == iEstado);
            }
            if (!string.IsNullOrEmpty(moneda))
            {
                if (moneda.Equals(Constantes.SIMBOLO_LOC))
                    query = query.Where(p => p.montoLOC != null);
                else if (moneda.Equals(Constantes.SIMBOLO_USD))
                    query = query.Where(p => p.montoUSD != null);
            }
            if (!string.IsNullOrEmpty(documento))
                query = query.Where(p => p.noDocumento.Contains(documento.Trim()));
            if (!string.IsNullOrEmpty(encargado))
                query = query.Where(p => p.encargado.Contains(encargado.Trim()));
            if (!string.IsNullOrEmpty(fecha))
            {
                DateTime dtFecha = DateTime.ParseExact(fecha.Trim(), Constantes.FORMATO_FECHA, CultureInfo.InvariantCulture);
                query = query.Where(p => p.fecha == dtFecha);
            }
            if (!string.IsNullOrEmpty(motivo))
            {
                int iMotivo = int.Parse(motivo);
                query = query.Where(p => p.idMotivo == iMotivo);
            }

            return query;
        }

        [HttpPost]
        public JsonResult AutorizarM(int idMovimiento)
        {
            try
            {
                Movimiento movimiento = UoW.movimientoRepository.FindFirst(p => p.idMovimiento == idMovimiento);
                //autorizar pagos
                foreach (Pago pago in movimiento.Pago)
                {
                    pago.estado = Constantes.ESTADOPAGO_AUTORIZADO;
                    UoW.pagoRepository.Edit(pago);
                    EnviarCorreoEC(pago.idParticipanteXEvento, idMovimiento, false);
                }
                //autorizar movimiento
                movimiento.estado = Constantes.ESTADOMOVIMIENTO_AUTORIZADO;
                UoW.movimientoRepository.Edit(movimiento);
                UoW.Save();
                AgregarBitacora("Autorización de movimiento (AutorizarM): " + idMovimiento.ToString());
                return Json(new { Result = "OK", Message = "Se ha autorizado exitosamente la boleta: " + movimiento.noDocumento, Estado = movimiento.estado });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error AutorizarM: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al autorizar movimiento." });
            }
        }

        [HttpPost]
        public JsonResult RechazarM(int idMovimiento)
        {
            try
            {
                Movimiento movimiento = UoW.movimientoRepository.FindFirst(p => p.idMovimiento == idMovimiento);
                //autorizar pagos
                foreach (Pago pago in movimiento.Pago)
                {
                    pago.estado = Constantes.ESTADOPAGO_RECHAZADO;
                    UoW.pagoRepository.Edit(pago);
                }
                //autorizar movimiento
                movimiento.estado = Constantes.ESTADOMOVIMIENTO_RECHAZADO;
                UoW.movimientoRepository.Edit(movimiento);
                UoW.Save();
                AgregarBitacora("Rechazo de movimiento (RechazarM): " + idMovimiento.ToString());
                return Json(new { Result = "OK", Message = "Se ha rechazado la boleta: " + movimiento.noDocumento, Estado = movimiento.estado });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error RechazarM: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al rechazar movimiento." });
            }
        }

        [HttpPost]
        public JsonResult TotalRecaudacion()
        {
            try
            {
                string sTotalIngreso;
                string sTotalAutorizado;
                string sCanjeado;
                string sDisponible;

                decimal? totalIngreso = UoW.movimientoRepository.FindBy(m =>
                            (m.idMotivo == Constantes.MOTIVODEPOSITO_RECAUDACION)
                            && (m.estado == Constantes.ESTADOMOVIMIENTO_INGRESADO))
                            .Sum(m => m.montoLOC);
                decimal? totalAutorizado = UoW.movimientoRepository.FindBy(m =>
                            (m.idMotivo == Constantes.MOTIVODEPOSITO_RECAUDACION)
                            && (m.estado == Constantes.ESTADOMOVIMIENTO_AUTORIZADO))
                            .Sum(m => m.montoLOC);
                decimal? canjeado = UoW.pagoRepository.FindBy(p =>
                            (p.idTipoPago == Constantes.TIPOPAGO_TRANSFERENCIARECAUDACION)
                            && (p.estado == Constantes.ESTADOPAGO_AUTORIZADO))
                            .Sum(p => p.montoLOC);
                sTotalIngreso = totalIngreso != null ? ((decimal)totalIngreso).ToString(Constantes.SPECIFIER_N) : "0.00";
                sTotalAutorizado = totalAutorizado != null ? ((decimal)totalAutorizado).ToString(Constantes.SPECIFIER_N) : "0.00";
                sCanjeado = canjeado != null ? ((decimal)canjeado).ToString(Constantes.SPECIFIER_N) : "0.00";
                decimal disponible = decimal.Parse(sTotalAutorizado) - decimal.Parse(sCanjeado);
                sDisponible = disponible.ToString(Constantes.SPECIFIER_N);


                return Json(new 
                { 
                    Result = "OK", 
                    TotalIngreso = sTotalIngreso,
                    TotalAutorizado = sTotalAutorizado,
                    Canjeado = sCanjeado,
                    Disponible = sDisponible
                });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error TotalRecaudacion: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener totales de recaudación." });
            }
        }
        #endregion

        #region "Acciones Traslado"
        [HttpPost]
        public JsonResult TrasladoList(int idParticipanteXEvento){
            try
            {
                var transferencias = UoW.transferenciaRepository.FindBy(d => d.idPagador == idParticipanteXEvento)
                    .Select(p => new
                    {
                        idTransferencia = p.idTransferencia,
                        idPagador = p.idPagador,
                        codigoReceptor = p.Receptor.codigo,
                        fecha = p.fecha,
                        montoLOC = p.montoLOC,
                        montoUSD = p.montoUSD,
                        observaciones = p.observaciones
                    }).ToList();
                var transferenciasCount = transferencias.Count;
                return Json(new { Result = "OK", Records = transferencias, TotalRecordCount = transferenciasCount });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error TrasladoList: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al obtener listado de traslados." });
            }
        }

        [HttpPost]
        public JsonResult CreateTraslado(Transferencia transferencia)
        {
            try
            {
                if (!ModelState.IsValid)
                {
                    AgregarBitacora("Error CreateTraslado: Formulario inválido");
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "¡El formulario no es válido! " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((transferencia.montoLOC == null && transferencia.montoUSD == null) ||
                    (transferencia.montoLOC != null && transferencia.montoUSD != null))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "Debe ingresar monto en dólares o quetzales (no ambos). " +
                        "Corrija e intente nuevemente."
                    });
                }
                if ((transferencia.montoLOC == null && transferencia.montoUSD <= 0) ||
                    (transferencia.montoUSD == null && transferencia.montoLOC <= 0))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto ingresado debe ser mayor que cero. " +
                        "Corrija e intente nuevemente."
                    });
                }

                //comparar participante
                if (transferencia.codigoReceptor != null)
                {
                    ParticipanteXEvento participante = UoW.participanteXEventoRepository
                        .FindFirst(t => t.codigo == transferencia.codigoReceptor.Trim() && t.estado == Constantes.ESTADOPARTICIPANTE_INSCRITO);
                    if (participante == null)
                    {
                        return Json(new
                        {
                            Result = "ERROR",
                            Message = "¡El participante que ha ingresado es inv&aacute;lido! <br/>" +
                                      "Corrija e intente nuevemente."
                        });
                    }
                }

                transferencia.fecha = DateTime.Now;
                return Json(new { Result = "OK", Record = transferencia });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error CreateTraslado: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al crear traslado." });
            }
        }

        [HttpPost]
        public JsonResult DeleteTraslado(int idTransferencia)
        {
            try
            {
                return Json(new { Result = "OK" });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error DeleteTraslado: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al eliminar traslado." });
            }
        }

        [HttpPost]
        public JsonResult TrasladarFondos(List<Transferencia> arrTraslado, int idPagador)
        {
            try
            {
                ParticipanteXEvento participante = UoW.participanteXEventoRepository.FindFirst(p => p.idParticipanteXEvento == idPagador);
                decimal disponibleLOC = participante.AbonoTotalLOC;
                decimal disponibleUSD = participante.AbonoTotalUSD;
                decimal trasladoLOC = 0;
                decimal trasladoUSD = 0;
                //obtener totales a trasladar
                foreach (var traslado in arrTraslado)
                {
                    trasladoLOC += traslado.montoLOC != null ? (decimal)traslado.montoLOC : 0;
                    trasladoUSD += traslado.montoUSD != null ? (decimal)traslado.montoUSD : 0;
                }

                //validar que el monto a trasladar sea igual que el monto disponible
                if ((trasladoLOC != disponibleLOC) || (trasladoUSD != disponibleUSD))
                {
                    return Json(new
                    {
                        Result = "ERROR",
                        Message = "El monto a trasladar debe ser igual que el monto disponible. " +
                        "Corrija e intente nuevemente."
                    });
                }
                //si el monto es correcto, realizar traslado
                foreach (Transferencia traslado in arrTraslado)
                {
                    ParticipanteXEvento receptor = UoW.participanteXEventoRepository.FindFirst(p => p.codigo == traslado.codigoReceptor);

                    //realizar abono a receptor
                    Pago pagoReceptor = new Pago();
                    pagoReceptor.estado = Constantes.ESTADOPAGO_AUTORIZADO;
                    pagoReceptor.fechaPago = DateTime.Now;
                    pagoReceptor.idParticipanteXEvento = receptor.idParticipanteXEvento;
                    pagoReceptor.idTipoPago = Constantes.TIPOPAGO_TRANSFERENCIAINTERNA;
                    pagoReceptor.montoLOC = traslado.montoLOC;
                    pagoReceptor.montoUSD = traslado.montoUSD;
                    pagoReceptor.observaciones = traslado.observaciones;
                    UoW.pagoRepository.Add(pagoReceptor);
                    
                    //dejar bitacora de transferencia
                    traslado.idReceptor = receptor.idParticipanteXEvento;
                    traslado.fecha = DateTime.Now;
                    traslado.idTipoPago = Constantes.TIPOPAGO_TRANSFERENCIAINTERNA;
                    UoW.transferenciaRepository.Add(traslado);
                }

                //cambiar de estado todos los pagos anteriores
                foreach (var pago in participante.Pago)
                {
                    bool bPago = pago.Movimiento.idMotivo != null ?
                                    pago.Movimiento.idMotivo == Constantes.MOTIVODEPOSITO_EVENTO
                                    : true;
                    if (bPago && (pago.estado == Constantes.ESTADOPAGO_AUTORIZADO))
                    {
                        pago.estado = Constantes.ESTADOPAGO_TRANSFERIDO;
                        UoW.pagoRepository.Edit(pago);
                    }
                }
                UoW.Save();

                AgregarBitacora("Traslado de fondos de participante (TrasladarFondos): " + participante.idParticipante.ToString());
                return Json(new { Result = "OK", Message = "Traslado de fondos de participante " + participante.codigo + " exitoso" });
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error TrasladarFondos: " + ex.Message + "--" + ex.Source);
                return Json(new { Result = "ERROR", Message = "Ocurrió un error al trasladar fondos." });
            }
        }
        #endregion

        #region "Funciones Adicionales"
        private bool RequiereBoleta(int tipoPago)
        {
            switch (tipoPago)
            {
                case Constantes.TIPOPAGO_CHEQUE:
                    return true;
                case Constantes.TIPOPAGO_EFECTIVO:
                    return true;
                default:
                    return false;
            }
        }

        private void EnviarCorreoEC(int idParticipanteXEvento, int idMovimiento = 0, bool bGuardar = true)
        {
            try
            {
                //obtener datos de participante
                ParticipanteXEvento participante = UoW.participanteXEventoRepository
                        .FindFirst(p => p.idParticipanteXEvento == idParticipanteXEvento);
                //obtener pagos realizados respecto al evento (autorizados)
                IEnumerable<PagoMail> pagosEvento = UoW.pagoRepository.FindBy(p =>
                        (p.ParticipanteXEvento.idParticipanteXEvento == idParticipanteXEvento)
                        && ((p.Movimiento == null) || (p.Movimiento.idMotivo == Constantes.MOTIVODEPOSITO_EVENTO))
                        && ((p.estado == Constantes.ESTADOPAGO_AUTORIZADO) || (p.idMovimiento == idMovimiento) ) )
                        .OrderBy(p => p.fechaPago).ToList()
                        .Select((p, index) => new PagoMail()
                        {
                            noPago = index + 1,
                            fecha = p.fechaPago.ToString(Constantes.FORMATO_FECHA),
                            moneda = p.montoLOC != null ? Constantes.SIMBOLO_LOC : Constantes.SIMBOLO_USD,
                            monto = p.montoLOC != null ? ((decimal)p.montoLOC).ToString(Constantes.SPECIFIER_N)
                                        : ((decimal)p.montoUSD).ToString(Constantes.SPECIFIER_N),
                            tipo = Constantes.TipoPago(p.idTipoPago),
                            documento = p.noDocumento != null ? p.noDocumento : string.Empty
                        });
                //obtener pagos de bus único (autorizado)
                Pago pb = UoW.pagoRepository.FindFirst(p =>
                        (p.ParticipanteXEvento.idParticipanteXEvento == idParticipanteXEvento)
                        && (p.Movimiento != null)
                        && (p.Movimiento.idMotivo == Constantes.MOTIVODEPOSITO_BUS)
                        && ((p.estado == Constantes.ESTADOPAGO_AUTORIZADO) || (p.idMovimiento == idMovimiento)));
                PagoMail pagoBus =  new PagoMail()
                        {
                            noPago = 1,
                            fecha = pb.fechaPago.ToString(Constantes.FORMATO_FECHA),
                            moneda = pb.montoLOC != null ? Constantes.SIMBOLO_LOC : Constantes.SIMBOLO_USD,
                            monto = pb.montoLOC != null ? ((decimal)pb.montoLOC).ToString(Constantes.SPECIFIER_N)
                                        : ((decimal)pb.montoUSD).ToString(Constantes.SPECIFIER_N),
                            tipo = Constantes.TipoPago(pb.idTipoPago),
                            documento = pb.noDocumento != null ? pb.noDocumento : string.Empty
                        };

                //armar correo
                String destinatario = participante.Participante.correoElectronico;
                String asunto = "Estado de Cuenta - Retiro Generación E - " + DateTime.Now.ToString(Constantes.FORMATO_FECHA);
                EstadoCuenta ec = new EstadoCuenta();
                ec.fecha = DateTime.Now.ToString(Constantes.FORMATO_FECHA);
                ec.pagosEvento = pagosEvento;
                ec.pagoBus = pagoBus;
                String texto = RenderPartialViewToString("_ECMailTemplate", ec);

                EnviarCorreo(destinatario, asunto, texto, bGuardar);
            }
            catch (Exception ex)
            {
                AgregarBitacora("Error EnviarCorreoEC: " + ex.Message + "--" + ex.Source);
            }
        }
        #endregion
    }
}
